﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Company.PackageExtjs
{
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Xml.Serialization;

    using FastColoredTextBoxNS;

    using Irony.Parsing;
    using Irony.Samples.Json;

    public partial class EditorControl : UserControl, IExtjsInterface
    {
        public EditorControl()
        {
            InitializeComponent();
        }

        public ParseTree LastProperParseTree { get; private set; }

        public FastColoredTextBox TextBox
        {
            get
            {
                return this.fastColoredTextBox1;
            }
        }

        private void fastColoredTextBox1_TextChangedDelayed(object sender, TextChangedEventArgs e)
        {
            timer.Stop();
            timer.Start();



        }

        private Parser compiler;

        private void EditorControl_Load(object sender, EventArgs e)
        {
            var grammar = new ExtJSGrammar();            
            this.compiler = new Parser(grammar);
            popupMenu = new AutocompleteMenu(TextBox);
            //popupMenu.Items.ImageList = imageList1;
            popupMenu.SearchPattern = @"[\w\.:=!<>]";
            popupMenu.AllowTabKey = true;
            
            popupMenu.Items.Dock=DockStyle.Fill;


            popupMenu.MaximumSize = new Size(300, 300);
            popupMenu.Size = new Size(300, 300);
            popupMenu.MinimumSize=new Size(300,300);
            List<AutocompleteItem> items = new List<AutocompleteItem>();

            

            try
            {
                XmlSerializer deserializer = new XmlSerializer(typeof(ExtClass[]));
                var txtReader = new StringReader(Resources.extjsDocumentation);
                var x = (ExtClass[])deserializer.Deserialize(txtReader);
                
                foreach (var extClass in x)
                {
                    items.Add(new ExtjsMethodAutoComplete(extClass.Name));
                    if (extClass.Methods != null)
                    foreach (var method in extClass.Methods)
                    {
                        items.Add(new ExtjsAutoComplete(this, method.Name, extClass.Name));
                        items.Add(new ExtjsFunctionAutoComplete(extClass.Name, method.Name));
                    }
                    if (extClass.Events != null)
                    foreach (var method in extClass.Events)
                    {
                        items.Add(new ExtjsAutoComplete(this, method.Name, extClass.Name));
                    }
                    if (extClass.Propertices != null)
                    foreach (var method in extClass.Propertices)
                    {
                        items.Add(new ExtjsAutoComplete(this, method.Name, extClass.Name));
                    }
                }

                popupMenu.Items.SetAutocompleteItems(items);
            }
            catch(Exception e1)
            {
                System.Console.WriteLine(e1);
            }
        }


        AutocompleteMenu popupMenu;

        private EllipseStyle style = new EllipseStyle();

        private void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                timer.Enabled = false;

                ParseTree = this.compiler.Parse(TextBox.Text);

                var idx = this.TextBox.GetStyleIndexMask(new[]{style});
                this.TextBox.ClearStyle(idx);
                if (ParseTree.HasErrors())
                {

                    var errors = ParseTree.ParserMessages;
                    errors.Reverse();
                    foreach (var resultMsg in errors)
                    {
                        var errorLocation = resultMsg.Location;
                        var len = this.TextBox.Lines[errorLocation.Line].Length;
                        var range = TextBox.GetRange(
                            new Place(0, errorLocation.Line), new Place(len, errorLocation.Line));
                        range.SetStyle(style);
                        var label = (ToolStripLabel)this.statusStrip1.Items[0];

                        label.Text = resultMsg.Message;
                        label.ForeColor = Color.Red;
                    }
                }
                else
                {
                    var label = (ToolStripLabel)this.statusStrip1.Items[0];

                    label.Text = "OK";
                    label.ForeColor = Color.Black;

                    this.LastProperParseTree = this.ParseTree;
                    
                    //TextBox.CurrentLineColor                    
                }
            }
            catch(Exception)
            {
                
            }
        }

        public ParseTree ParseTree { get; private set; }
    }

    class EllipseStyle : Style
    {
        public override void Draw(Graphics gr, Point position, Range range)
        {

            
            //get size of rectangle
            Size size = GetSizeOfRange(range);

            float x2 = position.X + size.Width + 1;
            float y = position.Y + size.Height;

            gr.DrawLine(Pens.Red, position.X, y, x2, y);

            /*
            //create rectangle
            var rect = new System.Drawing.
            //inflate it
            rect.Inflate(2, 2);
            //get rounded rectangle
            var path = GetRoundedRectangle(rect, 7);
            //draw rounded rectangle
            gr.DrawPath(Pens.Red, path);*/
        }
    }

    [Serializable]
    public class ExtPropertice : ExtElement
    {
    }

    [Serializable]
    public class ExtMethod : ExtElement
    {
    }

    [Serializable]
    public abstract class ExtElement
    {
        public string Name { get; set; }
    }

    [Serializable]
    public class ExtEvent : ExtElement
    {
    }


    [Serializable]
    public class ExtClass
    {
        public string Name { get; set; }
        public ExtEvent[] Events { get; set; }
        public ExtMethod[] Methods { get; set; }
        public ExtPropertice[] Propertices { get; set; }

    }


    static class ExtJsTree
    {


        internal static string GetLastExt(int line, int col, ParseTree parseTree)
        {
            var root = parseTree.Root;
            var x=SearchForElement(root.ChildNodes, line, col);

            

          return x;
        } 


        private static string SearchForElement(ParseTreeNodeList ptnl, int line, int col)
        {
            var childNodes = ptnl.OrderBy(p => p.Span.Location.Position);
            var result = string.Empty;
            ParseTreeNode nextLevel = null;
            while (childNodes != null && childNodes.Any())
            {
                var saveNext = false;
                foreach(var childNode in childNodes)
                {                    
                    if (saveNext)
                    {
                        saveNext = false;
                        result = childNode.ChildNodes[0].Token.Value.ToString();
                    }
                    else if (childNode.Span.Location.Line <= line && childNode.Span.Location.Column < col 
                        && childNode.ChildNodes.Any(p => p.Token!=null && p.Token.Value.Equals("Ext")))
                    {
                        saveNext = true;
                    }
                    else if (childNode.Span.Location.Line>line || (childNode.Span.Location.Line==line && childNode.Span.Location.Column>=col))
                    {
                        break;
                    }
                    nextLevel = childNode;
                }
                childNodes = nextLevel != null ? nextLevel.ChildNodes.OrderBy(p => p.Span.Location.Position) : null;

                //childNode.Where(p => p.Span.Location.Line < line && p.Span.Location.Column<col)
                //var minValue = childNodes.Where(p => p.Span.Location.Line < line).Max(p => p.Span.Location.Position);
                //var maxValue = childNodes.Where(p => p.Span.Location.Line > line).Min(p => p.Span.Location.Position);

                //var chosenElement = maxValue == 0
                //    ? childNodes.OrderByDescending(p => p.Span.Location.Position).FirstOrDefault()
                //    : childNodes.FirstOrDefault(p => p.Span.Location.Position == minValue);
                
                //if (chosenElement != null)
                //{
                //    if (chosenElement.Token != null && chosenElement.Token.Value.Equals("Ext"))
                //    {
                //        childNodes.OrderByDescending(p=>p.Span.Location.Position)
                //    }
                //    childNodes = chosenElement.ChildNodes;
                //}
                
            }            
            return result;
        }


        
    }

    class ExtjsAutoComplete : AutocompleteItem
    {
        string pattern;

        private readonly IExtjsInterface extjsInterface;

        private readonly string extName;

        public ExtjsAutoComplete(IExtjsInterface extjsInterface, string pattern, string extName)
            : base("")
        {
            this.extjsInterface = extjsInterface;
            this.pattern = pattern;
            this.extName = extName;
        }

        public ExtjsAutoComplete(IExtjsInterface extjsInterface, string extName)
            : this(extjsInterface, @"[\w\.:=!<>]", extName)
        {
        }

        public override CompareResult Compare(string fragmentText)
        {
            if (!Regex.IsMatch(pattern, "^" + fragmentText))
            {
                return CompareResult.Hidden;
            }

            var selectionStart = extjsInterface.TextBox.Selection.Start;
            var result ="Ext." + ExtJsTree.GetLastExt(selectionStart.iLine, selectionStart.iChar, extjsInterface.LastProperParseTree);

            Text = pattern;

            return result.Equals(extName, StringComparison.InvariantCultureIgnoreCase) ? CompareResult.Visible: CompareResult.Hidden;
        }

        public string InsertSpaces(string fragment)
        {
            var m = Regex.Match(fragment, pattern);
            if (m == null)
                return fragment;
            if (m.Groups[1].Value == "" && m.Groups[3].Value == "")
                return fragment;
            return (m.Groups[1].Value + " " + m.Groups[2].Value + " " + m.Groups[3].Value).Trim();
        }

        public override string ToolTipTitle
        {
            get
            {
                return Text;
            }
        }
    }




    class ExtjsMethodAutoComplete : AutocompleteItem
    {
        string pattern;

        public ExtjsMethodAutoComplete(string pattern)
            : base("")
        {

            this.pattern = pattern;
        }

        public ExtjsMethodAutoComplete()
            : this(@"[\w\.:=!<>]")
        {
        }

        

        public override CompareResult Compare(string fragmentText)
        {
            if (Regex.IsMatch(pattern, "^" + fragmentText))
            {

               // Text = InsertSpaces(fragmentText);
                //if (Text != fragmentText)
                Text = pattern;
                    return CompareResult.Visible;
            }
            return CompareResult.Hidden;
        }

        public string InsertSpaces(string fragment)
        {
            var m = Regex.Match(fragment, pattern);
            if (m == null)
                return fragment;
            if (m.Groups[1].Value == "" && m.Groups[3].Value == "")
                return fragment;
            return (m.Groups[1].Value + " " + m.Groups[2].Value + " " + m.Groups[3].Value).Trim();
        }

        public override string ToolTipTitle
        {
            get
            {
                return Text;
            }
        }
    }

    class ExtjsFunctionAutoComplete : AutocompleteItem
    {
        readonly string className;

        readonly string methodName;

        public ExtjsFunctionAutoComplete(string className, string methodName)
            : base("")
        {

            this.className = className;
            this.methodName = methodName;
        }



        public override CompareResult Compare(string fragmentText)
        {
            if (Regex.IsMatch(fragmentText, "^" + className + "[a-zA-Z0-9.]*"))
            {
                if (Regex.IsMatch(className+"."+methodName, "^" + fragmentText))
                {
                    Text = className + "." + methodName;
                    return CompareResult.Visible;
                }
            }
            return CompareResult.Hidden;
        }

        public override string ToolTipTitle
        {
            get
            {
                return methodName;
            }
        }
    }

    public interface IExtjsInterface
    {
        ParseTree ParseTree { get; }

        ParseTree LastProperParseTree { get; }

        FastColoredTextBox TextBox { get; }
    }

}
